﻿using DataModels.Models;
using GenericTools.CustomReporting;
using GenericTools.DataExporting;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace ReportingManagement.Components.Queries
{
    /// <summary>
    /// Interaction logic for CustomReports.xaml
    /// </summary>
    public partial class CustomReports : UserControl
    {
        public event EventHandler<InteroperableUserControlEventArgs> StatusChanged;

        private DataTable currentQueryData = null;
        private BackgroundWorker backgroundWorker = null;
        private QueryExecutionStatus queryExecutionStatus = null;

        private DateTime startOfRunningQuery;
        private DispatcherTimer timer = null;

        private Dictionary<string, string> previousQueryParameterValues = new Dictionary<string, string>();

        public CustomReports()
        {
            InitializeComponent();

            queryExecutionStatus = new QueryExecutionStatus();
            dataGridQueryStatus.DataContext = queryExecutionStatus;

            backgroundWorker = new BackgroundWorker();
            backgroundWorker.WorkerReportsProgress = false;
            backgroundWorker.WorkerSupportsCancellation = false;
            backgroundWorker.DoWork += backgroundWorker_DoWork;
            backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;

            timer = new DispatcherTimer();
            timer.Tick += timer_Tick;
            timer.Interval = new TimeSpan(0, 0, 1);

            // Register for events associated with quey managements
            QueryManagement.QueryOrCategoryModified += QueryManagement_QueryOrCategoryModified;
        }

        void QueryManagement_QueryOrCategoryModified(object sender, QueryManagementArgs e)
        {
            // Whenever this event fire will just reload the entire query category tree
            // This may not be the best solution but it works for NOW!!!
            
            object resource = TryFindResource("categoriesProvider");
            ObjectDataProvider categoriesProvider = resource as ObjectDataProvider;
            if (categoriesProvider != null) categoriesProvider.Refresh();
        }

        void timer_Tick(object sender, EventArgs e)
        {
            TimeSpan timeSpan = DateTime.Now - startOfRunningQuery;
            queryExecutionStatus.TimeTaken = string.Format("Time elapsed: {0:00}:{1:00}:{2:00}", timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds);
        }

        void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            timer.Stop();
            timer_Tick(null, null);

            if (e.Error == null)
            {
                queryExecutionStatus.ExecutionStatus = QueryExecutionStatus.QUERY_SUCCESS_TEXT;
                DataTable dataTable = e.Result as DataTable;
                currentQueryData = dataTable;
                queryExecutionStatus.RowsReturned = string.Format("{0} rows", dataTable.Rows.Count);

                dataGridResults.ItemsSource = dataTable.DefaultView;
                btnExportToExcel.IsEnabled = dataTable.Rows.Count > 0;
                btnTransformToChart.IsEnabled = dataTable.Rows.Count > 0;

                var args = new InteroperableUserControlEventArgs
                {
                    StatusText = QueryExecutionStatus.QUERY_SUCCESS_TEXT,
                    Type = InteroperableUserControlEventArgs.StatusType.Text
                };
                if (StatusChanged != null) StatusChanged(this, args);
            }
            else
            {
                queryExecutionStatus.ExecutionStatus = QueryExecutionStatus.QUERY_ERROR_TEXT;
                string message = e.Error.Message;
                var soapException = e.Error as System.Web.Services.Protocols.SoapException;
                if (soapException != null)
                {
                    // We will assume for every SoapException there will stack trace as part of its message.
                    // But we don't want to display the trace, so We'll assume the message ends before the
                    // appearance of 'at System.' phrase
                    int targetIndex = soapException.Message.IndexOf("at System.");
                    if (targetIndex != -1) message = soapException.Message.Substring(0, targetIndex);
                    message = message.Trim();
                }

                MessageBox.Show(message, GenericTools.AppConfiguration.ApplicationName,
                                MessageBoxButton.OK, MessageBoxImage.Error);

                var args = new InteroperableUserControlEventArgs
                {
                    StatusText = QueryExecutionStatus.QUERY_ERROR_TEXT,
                    Type = InteroperableUserControlEventArgs.StatusType.Text
                };
                if (StatusChanged != null) StatusChanged(this, args);
            }

            btnRun.IsEnabled = true;
            cboReport.IsEnabled = true;

            // Restore the event processing for listbox items
            for (int i = 0; i < lstReportCategories.Items.Count; i++)
            {
                ListBoxItem item = lstReportCategories.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
                item.IsHitTestVisible = true;
                item.ClearValue(Control.BackgroundProperty);
            }
        }

        void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            object[] arguments = e.Argument as object[];
            Query query = (Query)arguments[0];
            List<string> parameterValues = (List<string>)arguments[1];

            DataTable dataTable = ReportQuerying.RunQuery(query, parameterValues);
            e.Result = dataTable;
        }

        private void btnRun_Click(object sender, RoutedEventArgs e)
        {
            Query query = cboReport.SelectedItem as Query;
            if (query != null)
            {
                List<string> parameterValues = new List<string>();
                if (query.QueryParameters.Count > 0)
                {
                    var parameterCollector = new ReportParameterCollector(query.QueryParameters, previousQueryParameterValues);
                    
                    // Let's try to find the parent of this windows, so that when is shown it always be
                    // the modal dialog
                    Window mainWindowWpf = Window.GetWindow(this);
                    if (mainWindowWpf != null) parameterCollector.Owner = mainWindowWpf;
                    else
                    {
                        var mainWindowFormHandle = MainWindow.GetActiveWindow();
                        var helper = new System.Windows.Interop.WindowInteropHelper(parameterCollector);
                        helper.Owner = mainWindowFormHandle;
                    }

                    bool? okToContinue = parameterCollector.ShowDialog();       
                    if (!okToContinue.HasValue) return;
                    if (okToContinue.HasValue && okToContinue.Value == false) return;
                    else
                    {
                        foreach (var param in query.QueryParameters)
                        {
                            string parameterValue = parameterCollector.ParameterValues
                                .Where(v => v.Name == param.Name)
                                .SingleOrDefault().Value;

                            parameterValues.Add(parameterValue);
                        }

                        // Here we are saving the parameter values, in case they pop up again
                        foreach (var param in parameterCollector.ParameterValues) 
                        {
                            string key = param.Name;
                            previousQueryParameterValues[key] = param.Value;
                        }
                    }
                }                

                btnRun.IsEnabled = false;
                cboReport.IsEnabled = false;

                // This will not disable ListBox but its items
                // We will also create a light gray background
                for (int i = 0; i < lstReportCategories.Items.Count; i++)
                {
                    ListBoxItem item = lstReportCategories.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
                    item.IsHitTestVisible = false;
                    item.Background = new SolidColorBrush(Colors.WhiteSmoke);
                }

                object[] arguments = new object[] { query, parameterValues };

                queryExecutionStatus.ExecutionStatus = QueryExecutionStatus.QUERYING_TEXT;
                startOfRunningQuery = DateTime.Now;
                timer.Start();
                backgroundWorker.RunWorkerAsync(arguments); 
                var args = new InteroperableUserControlEventArgs
                {
                    StatusText = QueryExecutionStatus.QUERYING_TEXT,
                    Type = InteroperableUserControlEventArgs.StatusType.Text
                };
                if (StatusChanged != null) StatusChanged(this, args);
            }
        }

        private void btnExportToExcel_Click(object sender, RoutedEventArgs e)
        {
            if (currentQueryData != null)
            {
                try
                {
                    ExcelFileExporting excelExporter = new ExcelFileExporting(currentQueryData);
                    string outputFile = excelExporter.Export();
                    System.Diagnostics.Process.Start(outputFile);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, GenericTools.AppConfiguration.ApplicationName, 
                        MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private void btnRefreshCategories_Click(object sender, RoutedEventArgs e)
        {
            object resource = TryFindResource("categoriesProvider");
            ObjectDataProvider categoriesProvider = resource as ObjectDataProvider;
            if (categoriesProvider != null)
            {
                categoriesProvider.DataChanged += categoriesProvider_DataChanged;
                btnRefreshCategories.IsEnabled = false;
                categoriesProvider.Refresh();
                var args = new InteroperableUserControlEventArgs
                {
                    StatusText = "Loading report categories. Please wait...",
                    Type = InteroperableUserControlEventArgs.StatusType.Text
                };
                if (StatusChanged != null) StatusChanged(this, args);
            }
        }

        void categoriesProvider_DataChanged(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
            {
                btnRefreshCategories.IsEnabled = true;
                var args = new InteroperableUserControlEventArgs
                {
                    StatusText = "Ready",
                    Type = InteroperableUserControlEventArgs.StatusType.Text
                };
                if (StatusChanged != null) StatusChanged(this, args);
            });
        }
    }

    public class QueryExecutionStatus : ModelBase
    {
        public const string QUERYING_TEXT = "Executing Query. Please wait...";
        public const string QUERY_SUCCESS_TEXT = "Query executed successfully.";
        public const string QUERY_ERROR_TEXT = "Query Completed with errors.";

        private string _ExecutionStatus;
        private string _TimeTaken;
        private string _RowsReturned;
        private Brush _TextColor;

        public string ExecutionStatus
        {
            get { return _ExecutionStatus; }
            set
            {
                _ExecutionStatus = value;
                OnPropertyChanged("ExecutionStatus");
                UpdateTextColor();
            }
        }

        public string TimeTaken
        {
            get { return _TimeTaken; }
            set
            {
                _TimeTaken = value;
                OnPropertyChanged("TimeTaken");
            }
        }

        public string RowsReturned
        {
            get { return _RowsReturned; }
            set
            {
                _RowsReturned = value;
                OnPropertyChanged("RowsReturned");
            }
        }

        public Brush TextColor
        {
            get { return _TextColor; }
            set
            {
                _TextColor = value;
                OnPropertyChanged("TextColor");
            }
        }

        private void UpdateTextColor()
        {
            switch (_ExecutionStatus)
            {
                case QUERY_SUCCESS_TEXT:
                    TextColor = Brushes.LimeGreen;
                    break;

                case QUERY_ERROR_TEXT:
                    TextColor = Brushes.Red;
                    break;

                case QUERYING_TEXT:
                default:
                    TextColor = Brushes.Blue;
                    break;
            }
        }
    }
}
